/*
 * Created on Nov 24, 2005
 */
package com.mgmaps.utils;

import javax.microedition.lcdui.*;
import javax.bluetooth.*;
import com.mgmaps.app.APPCommands;
import com.mgmaps.app.APPMidlet;

import java.io.*;
import java.util.Vector;

/**
 * This class implements bluetooth discovery and OBEX utility methods.
 *
 * @author CristiS
 * @version 1.0
 */
public class Bluetooth implements IBluetooth, DiscoveryListener, CommandListener {
  
  // command listener
  private CommandListener listener;
 
  // bluetooth devices
  private Vector devices;
  // bluetooth services
  private Vector services;
  // discovery agent
  private DiscoveryAgent agent;
  // selected device
  private RemoteDevice selectedDevice;
  // predefined UUID groups
  // private final UUID[] serialUUID1 = new UUID[] {new UUID(0x0100)};
  private final UUID[] serialUUID1 = new UUID[] {new UUID(0x0003)};
  private final UUID[] serialUUID2 = new UUID[] {new UUID(0x1101)};
  private UUID[] savedUUIDs;
  
  final List bdList = new List(LS.get("bt.dev"), Choice.IMPLICIT);
  
  /**
   * Notifier for sending callback commands.
   */
//  private class DiscoveryNotifier implements Runnable {
//    
//    private Command cmd;
    
    /**
     * Constructor for DiscoveryNotifier.
     *
     * @param cmd command selected
     */
//    public DiscoveryNotifier(Command cmd) {
//      this.cmd = cmd;
//    }
    
    /**
     * Action.
     * 
     * @see java.lang.Runnable#run()
     */
//    public void run() {
//      listener.commandAction(cmd, bdList);
//    }
//  }

  
  /**
   * Constructor for BlueTools.
   */
  public Bluetooth() {
    super();
    
    // build UI
    devices = new Vector();
    services = new Vector();
    
    bdList.setSelectCommand(APPCommands.instance.btSelectCommand);
    bdList.addCommand(APPCommands.instance.btRefreshCommand);
    bdList.addCommand(APPCommands.instance.btCancelCommand);
    bdList.setCommandListener(this);
    
    populateList();
  }
  
  /**
   * Get the URL of the first discovered service.
   * 
   * @return the URL
   * @throws Exception if nothing discovered
   */
  public String getURL() throws Exception {
    if (services.size() > 0) {
      ServiceRecord sr = (ServiceRecord) services.elementAt(0);
      return sr.getConnectionURL(ServiceRecord.NOAUTHENTICATE_NOENCRYPT, false);
    }
    else if (selectedDevice != null)
      // just build a URL with :1
      return "btspp://" + selectedDevice.getBluetoothAddress() + ":1";
    else
      throw new Exception("No Bluetooth Service Discovered");
  }
  
  /**
   * Populate the bluetooth device list.
   */
  private void populateList() {
    bdList.deleteAll();
    int sz = devices.size();
    if (sz == 0)
      bdList.append(LS.get("bt.nodev"), null);
    else {
      // append device names
      try {
        for (int i=0; i<sz; i++)      
          bdList.append(((RemoteDevice) devices.elementAt(i)).getFriendlyName(false), null);
      }
      catch (IOException ex) {
        ExtLogger.handle(ex, false, "populateList()");
      }
    }
    bdList.setSelectedIndex(0, true);
  }
  
  /**
   * Populate the bluetooth device list with a message (and move the selection).
   * 
   * @param msg the message to display
   */
  private void populateList(String msg) {
    bdList.deleteAll();
    bdList.append(msg, null);
    bdList.setSelectedIndex(0, true);
  }
  
  /**
   * Show the bluetooth device screen. Run discovery if no device is found yet.
   * 
   * @param uuids UUIDs to search
   */
  public void show() {
    // populate the UI
    populateList();
    // show it
    Display.getDisplay(APPMidlet.instance).setCurrent(bdList);
    // run discovery
    if (devices.size() == 0)
      runDiscovery();
  }
  
  /**
   * Show the GUI for push profile UUIDs.
   * 
   * @see com.mgmaps.utils.IBluetooth#showGPS()
   */
  public void showGPS() {
    show();
  }

  /**
   * Run device discovery.
   */
  public void runDiscovery() {
    // clear previous values
    devices.removeAllElements();
    selectedDevice = null;
    
    try {
      // set discovery params and run discovery
      LocalDevice device = LocalDevice.getLocalDevice(); // obtain reference to singleton
      device.setDiscoverable(DiscoveryAgent.GIAC); // set Discover Mode
      agent = device.getDiscoveryAgent();
      agent.startInquiry(DiscoveryAgent.GIAC, this);
      populateList(LS.get("bt.searching"));
    }
    catch (Exception ex) {
      ExtLogger.handle(ex, false, "Error with device discovery");
    }
  }

  /**
   * A device was discovered.
   * @param remoteDevice device discovered
   * @param deviceClass device class
   * @see javax.bluetooth.DiscoveryListener#deviceDiscovered(javax.bluetooth.RemoteDevice, javax.bluetooth.DeviceClass)
   */
  public void deviceDiscovered(RemoteDevice remoteDevice, DeviceClass deviceClass) {
    try {
      remoteDevice.getFriendlyName(false);
    }
    catch(IOException ie) { }
    // add to list
    if (devices.indexOf(remoteDevice) == -1)
      devices.addElement(remoteDevice);
  }
  
  /**
   * Inquiry completed.
   * 
   * @param complete return code
   * @see javax.bluetooth.DiscoveryListener#inquiryCompleted(int)
   */
  public void inquiryCompleted(int complete) {
    populateList();
  }

  /**
   * Services discovered.
   * 
   * @param transId id
   * @param records service records
   * @see javax.bluetooth.DiscoveryListener#servicesDiscovered(int, javax.bluetooth.ServiceRecord[])
   */
  public void servicesDiscovered(int transId, ServiceRecord[] records) {
    // add all records to services
    for (int i=0; i<records.length; i++)
      services.addElement(records[i]);
  }
  
  /**
   * Service discovery complete.
   * 
   * @param transId id
   * @param complete completion return code
   * @see javax.bluetooth.DiscoveryListener#serviceSearchCompleted(int, int)
   */
  public void serviceSearchCompleted(int transId, int complete) {
    // handle selection only if at least one service was discovered
    if (services.size() > 0)
      // Display.getDisplay(PaginiAurii.instance).callSerially(new DiscoveryNotifier(BT_COMPLETE));
      listener.commandAction(APPCommands.instance.btCompleteCommand, bdList);
    else if (savedUUIDs == serialUUID1) {
      savedUUIDs = serialUUID2;
      try {
        // int[] attrSet = {0x0100, 0x0003, 0x0004};
        agent.searchServices(null, savedUUIDs, selectedDevice, this);
      }
      catch (Exception ex) {
        ExtLogger.handle(ex, false, "Error with service discovery");
      }
    }
    else {
      ExtLogger.debug("Serial port services not found, connecting to the first service available.");
      // Display.getDisplay(APPMidlet.instance).callSerially(new DiscoveryNotifier(BT_COMPLETE));
      listener.commandAction(APPCommands.instance.btCompleteCommand, bdList);
      // midlet.showAlert(I18n._(130), I18n._(164), false);
    }
  }
  
  /**
   * Called when a command is selected.
   * 
   * @param command command selected
   * @param disp current displayable
   * @see javax.microedition.lcdui.CommandListener#commandAction(javax.microedition.lcdui.Command, javax.microedition.lcdui.Displayable)
   */
  public void commandAction(Command command, Displayable disp) {
    // responds only to bdList events
    if (disp == bdList) {
      // refresh starts the device inquiry
      if (command == APPCommands.instance.btRefreshCommand)
        runDiscovery();
      
      // cancel just pops displayable
      else if (command == APPCommands.instance.btCancelCommand)
        // Display.getDisplay(PaginiAurii.instance).callSerially(new DiscoveryNotifier(BT_CANCEL));
        listener.commandAction(APPCommands.instance.btCancelCommand, bdList);
      
      // device selected, start service inquiry
      else if (command == APPCommands.instance.btSelectCommand || command == List.SELECT_COMMAND) {
        // don't do anything if no devices are found
        if (devices.size() == 0)
          return;
        selectedDevice = (RemoteDevice) devices.elementAt(bdList.getSelectedIndex());
        services.removeAllElements();
        
        savedUUIDs = serialUUID1;
        try {
          // int[] attrSet = {0x0100, 0x0003, 0x0004};
          agent.searchServices(null, savedUUIDs, selectedDevice, this);
        }
        catch (Exception ex) {
          ExtLogger.handle(ex, false, "Error with service discovery");
        }
      }
    }
  }

  /**
   * Set the command listener.
   * 
   * @param listener command listener
   * @see com.mgmaps.utils.IBluetooth#setListener(javax.microedition.lcdui.CommandListener)
   */
  public void setListener(CommandListener listener) {
    this.listener = listener;
  }
}
